/**
 * Copyright (C) 2009 Marcel Urbanek
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package de.urbiworx.sugar;

import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.logging.Logger;

import javax.faces.context.FacesContext;

import com.google.inject.Key;
import com.google.inject.name.Named;
import com.google.inject.name.Names;

public class SugarScopeHelper {

	protected final static String SUGARMAP="de.urbiworx.sugar";
	protected final static String TWILIGHT="twilightzone";
	protected final static String MULTIVERSE="multiverse";
	protected final static String CLIENTLIST="de.urbiworx.sugar.clientlist";
	
	static Logger logger=Logger.getLogger("de.urbiworx.sugar");
	
	public final static String REQUEST_SCOPE_NAME="request";
	public final static String SESSION_SCOPE_NAME="session";
	public final static String APPLICATION_SCOPE_NAME="application";
	public final static String TWILIGHT_SCOPE_NAME="twilight";

	public final static int REQUEST_SCOPE=1;
	public final static int SESSION_SCOPE=2;
	public final static int APPLICATION_SCOPE=3;
	public final static int TWILIGHT_SCOPE=4;
	public final static int NONE_SCOPE=-1;
	
	public static Collection <SugarConfigurationEntry> getClientList()
	{
		FacesContext context=FacesContext.getCurrentInstance();
		Collection <SugarConfigurationEntry> tempClientList=(Collection<SugarConfigurationEntry>) context.getExternalContext().getRequestMap().get(CLIENTLIST);
		if (tempClientList==null)
		{
			tempClientList = new LinkedList<SugarConfigurationEntry>();
			context.getExternalContext().getRequestMap().put(CLIENTLIST,tempClientList);
		}
		return tempClientList;
	}

	
	
	
	public static Map<SugarConfigurationEntry,Object> getClientEntriesForMap(Map<Key,Object> map)
	{
		Map<SugarConfigurationEntry,Object>  entries=new HashMap<SugarConfigurationEntry,Object> ();
		for (Map.Entry<Key,Object> mapEntry:map.entrySet())
		{
			Key key=mapEntry.getKey();
			SugarConfigurationEntry entry=convertKeyToSugarEntry(key);
			if (entry.isClient())
			{
				entries.put(entry,mapEntry.getValue());
			}
		}
		return entries;
	}
	public static SugarConfigurationEntry convertKeyToSugarEntry(Key key)
	{		
		Named namedAnnotation=(Named)key.getAnnotation();
		
		if (namedAnnotation==null)
		{
			return SugarConfigurationHolder.getInstance().getConfigurationByClass().get(key.getTypeLiteral());
		}
		else
		{
			String name=namedAnnotation.value();
			return SugarConfigurationHolder.getInstance().getConfiguration().get(name);
		}
	}
	
	public static Key convertSugarEntryToKey(SugarConfigurationEntry entry)
	{
		FacesContext context=FacesContext.getCurrentInstance();
		try {
			return Key.get(context.getClass().getClassLoader().loadClass(entry.getType()),Names.named(entry.getName()));
		} catch (ClassNotFoundException e) {
			throw new SugarException("Class not found for type:"+entry.getType()+" File:"+entry.getFilename()+" Line:"+entry.getLine());
		}		
	}
	
	public static Object getObjectForKey(Key key)
	{
		SugarConfigurationEntry entry=convertKeyToSugarEntry(key);
		return getScopeMapForScope(entry.getScope(),entry.isMultiverse()).get(key);
	}
	
	public static Object getObjectForSugarEntry(SugarConfigurationEntry entry) {
		Key key=convertSugarEntryToKey(entry);
		return getScopeMapForScope(entry.getScope(),entry.isMultiverse()).get(key);
	}

	public static void setObjectForKey(Object object,Key key)
	{
		SugarConfigurationEntry entry=convertKeyToSugarEntry(key);
		getScopeMapForScope(entry.getScope(),entry.isMultiverse()).put(key,object);
	}
	
	public static void setObjectForSugarEntry(Object object,SugarConfigurationEntry entry){
		Key key=convertSugarEntryToKey(entry);
		getScopeMapForScope(entry.getScope(),entry.isMultiverse()).put(key,object);
	}

	
	public static Map<Key,Object>getScopeMapForScope(int scope,boolean multiverse)
	{
		Map<Key,Object> map=null;
		if (!multiverse)
		{
			switch(scope)
			{
				case(REQUEST_SCOPE):
					map = getSugarMapRequest();
					break;
				case(SESSION_SCOPE):
					map = getSugarMapSession();
					break;
				case(TWILIGHT_SCOPE):
					map = getSugarMapTwilight();
					break;
				case(APPLICATION_SCOPE):
					map = getSugarMapApplication();
					break;
			}
		}
		else
		{
			switch(scope)
			{
				case(REQUEST_SCOPE):
					map = getSugarMapRequestMultiverse();
					break;
				case(SESSION_SCOPE):
					map = getSugarMapSessionMultiverse();
					break;
				case(APPLICATION_SCOPE):
					map = getSugarMapApplicationMultiverse();
					break;
			}
		}
				
		return map;
	}
	
	public static Map<Key, Object> getSugarMapRequest() {
		FacesContext context=FacesContext.getCurrentInstance();
		Map <String,Object> requestMap=context.getExternalContext().getRequestMap();
		Map <Key,Object> sugarmap=(Map<Key, Object>)requestMap.get(SUGARMAP);
		if (sugarmap==null)
		{
			sugarmap=new HashMap<Key,Object>();
			requestMap.put(SUGARMAP, sugarmap);
		}
		return sugarmap;
	}
	
	public static Map<Key, Object> getSugarMapSession() {
		FacesContext context=FacesContext.getCurrentInstance();
		Map <String,Object> sessionMap=context.getExternalContext().getSessionMap();
		Map <Key,Object> sugarmap=(Map<Key, Object>)sessionMap.get(SUGARMAP);
		if (sugarmap==null)
		{
			sugarmap=new HashMap<Key,Object>();
			sessionMap.put(SUGARMAP, sugarmap);
		}
		return sugarmap;
	}
	
	public static Map<Key, Object> getSugarMapApplication() {
		FacesContext context=FacesContext.getCurrentInstance();
		
		if (context!=null){
			Map <String,Object> applicationMap=context.getExternalContext().getApplicationMap();		
			Map <Key,Object> sugarmap=(Map<Key, Object>)applicationMap.get(SUGARMAP);
			if (sugarmap==null)
			{
				synchronized(SUGARMAP){							
					sugarmap=(Map<Key, Object>)applicationMap.get(SUGARMAP);
					if (sugarmap==null){
						sugarmap=new HashMap<Key, Object>();							
						applicationMap.put(SUGARMAP, sugarmap);
					}
				}
			}
			return sugarmap;
		}
		else if (SugarContextListener.getServletContext()!=null)
		{
			Map <Key,Object> sugarmap=(Map<Key, Object>)SugarContextListener.getServletContext().getAttribute(SUGARMAP);
			if (sugarmap==null)
			{
				sugarmap=new HashMap<Key,Object>();
				SugarContextListener.getServletContext().setAttribute(SUGARMAP, sugarmap);
			}
			return sugarmap;
		}
		throw new RuntimeException("Error while trying to get Application Scope Map, you are currently not in a web context");
	}
	
	public static Map<Key, Object> getSugarMapTwilight() {
		FacesContext context=FacesContext.getCurrentInstance();
		Map <String,Object> sessionMap=context.getExternalContext().getSessionMap();
		Map <Key,Object> sugarmap=(Map<Key, Object>)sessionMap.get(SUGARMAP+"."+TWILIGHT);
		if (sugarmap==null)
		{
			sugarmap=new HashMap<Key,Object>();
			sessionMap.put(SUGARMAP+"."+TWILIGHT, sugarmap);
		}
		return sugarmap;
	}
	
	public static void removeSugarMapTwilight() {
		FacesContext context=FacesContext.getCurrentInstance();
		Map <String,Object> sessionMap=context.getExternalContext().getSessionMap();
		if (sessionMap.containsKey(SUGARMAP+"."+TWILIGHT))
		{
			sessionMap.remove(SUGARMAP+"."+TWILIGHT);	
		}
	}
	
	public static Map<Key, Object> getSugarMapRequestMultiverse() {
		FacesContext context=FacesContext.getCurrentInstance();
		String view=context.getViewRoot().getViewId();
		Map <String,Object> requestMap=context.getExternalContext().getRequestMap();
		Map <String,Object> multiversemap=(Map<String, Object>)requestMap.get(SUGARMAP+"."+MULTIVERSE);
		if (multiversemap==null)
		{
			multiversemap=new HashMap<String,Object>();
			requestMap.put(SUGARMAP+"."+MULTIVERSE, multiversemap);
		}
		Map <Key,Object> sugarmap=(Map<Key, Object>)multiversemap.get(view);
		if (sugarmap==null)
		{
			sugarmap=new HashMap<Key,Object>();
			multiversemap.put(view, sugarmap);
		}
		return sugarmap;
	}
	
	public static Map<Key, Object> getSugarMapSessionMultiverse() {
		FacesContext context=FacesContext.getCurrentInstance();
		String view=context.getViewRoot().getViewId();
		Map <String,Object> sessionMap=context.getExternalContext().getSessionMap();
		Map <String,Object> multiversemap=(Map<String, Object>)sessionMap.get(SUGARMAP+"."+MULTIVERSE);
		if (multiversemap==null)
		{
			multiversemap=new HashMap<String,Object>();
			sessionMap.put(SUGARMAP+"."+MULTIVERSE, multiversemap);
		}
		Map <Key,Object> sugarmap=(Map<Key, Object>)multiversemap.get(view);
		if (sugarmap==null)
		{
			sugarmap=new HashMap<Key,Object>();
			multiversemap.put(view, sugarmap);
		}
		return sugarmap;
	}
	
	public static Map<Key, Object> getSugarMapApplicationMultiverse() {
		FacesContext context=FacesContext.getCurrentInstance();
		String view=context.getViewRoot().getViewId();
		Map <String,Object> applicationMap=context.getExternalContext().getApplicationMap();
		Map <String,Object> multiversemap=(Map<String, Object>)applicationMap.get(SUGARMAP+"."+MULTIVERSE);
		if (multiversemap==null)
		{
			multiversemap=new HashMap<String,Object>();
			applicationMap.put(SUGARMAP+"."+MULTIVERSE, multiversemap);
		}
		Map <Key,Object> sugarmap=(Map<Key, Object>)multiversemap.get(view);
		if (sugarmap==null)
		{
			sugarmap=new HashMap<Key,Object>();
			multiversemap.put(view, sugarmap);
		}
		return sugarmap;
	}

	public static void removeSugarMapTwilightMultiverse() {
		FacesContext context=FacesContext.getCurrentInstance();
		Map <String,Object> sessionMap=context.getExternalContext().getSessionMap();
		if (sessionMap.containsKey(SUGARMAP+"."+TWILIGHT))
		{
			sessionMap.remove(SUGARMAP+"."+TWILIGHT);	
		}		
	}
	
}
